const MongoClient = require('mongodb').MongoClient;
const ObjectId = require('mongodb').ObjectId;
const config = require('./config'); // 引入Mongo数据库配置文件

class DB {

  /**
   * 静态方法，单例化DB类
   * @returns {DB}
   */
  static getInstance() {
    if (!DB.instance) {
      DB.instance = new DB();
    }
    return DB.instance;
  }

  constructor() {
    this.dbClient = null; // 保存db连接对象
    this.connect();
  }

  /**
   * 获取_id对象
   * @param _id
   * @returns {*}
   */
  getObjId(_id) {
    return new ObjectId(_id);
  }

  /**
   * 连接Mongo数据库
   * @returns {Promise<any>}
   */
  connect() {
    return new Promise((resolve, reject) => {
      if (this.dbClient) {
        resolve(this.dbClient);
      } else {
        MongoClient.connect(config.DB_URL, {useNewUrlParser: true}, (err, client) => {
          if (err) {
            console.log('数据库连接失败...');
            reject(err);
          } else {
            this.dbClient = client.db(config.DB_NAME);
            resolve(this.dbClient)
          }
        });
      }
    });
  }

  /**
   * 查询数据
   * @param colName
   * @param json
   * @returns {Promise<any>}
   */
  find(colName, filter = {}) {
    return new Promise(async (resolve, reject) => {
      const dbClient = await this.connect();
      dbClient.collection(colName).find(filter).toArray((err, docs) => {
        if (err) {
          console.log(`${colName} 集合查询数据失败`);
          reject(err);
        } else {
          resolve(docs);
        }
      })
    });
  }

  /**
   * 查询分页
   * @param colName
   * @param currentPage
   * @param pageSize
   * @param filter
   * @returns {Promise<any>}
   */
  page(colName, filter = {}, currentPage = 1, pageSize = 20) {
    return new Promise(async (resolve, reject) => {
      const dbClient = await this.connect();
      const totalCounts = await dbClient.collection(colName).countDocuments(filter);
      // console.log('totalCounts', totalCounts);
      if (totalCounts > 0) {
        const lastPage = Math.ceil(totalCounts / pageSize);
        currentPage = currentPage < 1 ? 1 : currentPage > lastPage ? lastPage : currentPage;
        const skip = pageSize * (currentPage - 1);
        dbClient.collection(colName).find(filter).skip(skip).limit(pageSize).toArray((err, docs) => {
          if (err) {
            console.log(`${colName} 集合查询数据失败`);
            reject(err);
          } else {
            resolve({
              totalCounts,
              currentPage,
              pageSize,
              list: docs
            });
          }
        })
      } else {
        resolve({
          totalCounts: 0,
          currentPage: 1,
          pageSize,
          list: []
        });
      }
    });
  }

  /**
   * 新增数据
   * @param colName
   * @param data
   * @returns {Promise<any>}
   */
  insert(colName, data) {
    return new Promise(async (resolve, reject) => {
      let insertMethod = '';
      switch (Object.prototype.toString.call(data)) { // 判断数据类型是对象还是数组
        case '[object Object]':
          insertMethod = 'insertOne'; // 如果为对象，使用insertOne方法
          break;
        case '[object Array]':
          insertMethod = data.length > 0 ? 'insertMany' : ''; // 如果为数组且长度大于0，使用insertMany方法
          break;
        default:
          break;
      }
      if (insertMethod) {
        const dbClient = await this.connect();
        dbClient.collection(colName)[insertMethod](data, (err, result) => {
          if (err) {
            console.log(`向 ${colName} 集合新增数据失败...`);
            reject(err);
          } else {
            resolve(result.ops); // ops为包含_id的新插入数据，类型为数组
          }
        })
      } else {
        console.log(`不能向 ${colName} 集合插入空数据`);
        resolve(data);
      }
    });
  }

  /**
   * 更新数据
   * @param colName
   * @param filter
   * @param data
   * @param multi
   * @returns {Promise<any>}
   */
  update(colName, filter, data, multi = false) {
    return new Promise(async (resolve, reject) => {
      const dbClient = await this.connect();
      const updateMethod = multi ? 'updateMany' : 'updateOne'; // 更新首条或全部更新
      dbClient.collection(colName)[updateMethod](filter, {$set: data}, (err, result) => {
        if (err) {
          console.log(`${colName} 集合更新数据失败`);
          reject(err);
        } else {
          resolve(result.result);
        }
      })
    });
  }

  remove(colName, filter) {
    return new Promise(async (resolve, reject) => {
      const dbClient = await this.connect();
      dbClient.collection(colName).removeOne(filter, (err, result) => {
        if (err) {
          console.log(`${colName} 集合删除数据失败`);
          reject(err);
        } else {
          resolve(result.result);
        }
      })
    });
  }

}

module.exports = DB.getInstance();